package concurrent;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 使用JDK重入锁（ReentrantLock）添加添加条件判断
 * 从整体上来看Object的wait和notify/notify是与对象监视器配合完成线程间的等待/通知机制，而Condition与Lock配合完成等待通知机制，
 * 前者是java底层级别的，后者是语言级别的，具有更高的可控制性和扩展性。
 *
 * Condition能够支持不响应中断，而通过使用Object方式不支持
 * Condition能够支持多个等待队列（new 多个Condition对象），而Object方式只能支持一个
 * Condition能够支持超时时间的设置，而Object不支持
 *
 * @author jack.wu
 * @since 2019/9/7
 */
public class ConditionTest {

    /**
     * 重入锁
     */
    private final Lock lock = new ReentrantLock();
    /**
     * 写条件
     */
    private final Condition writeCondition = lock.newCondition();
    /**
     * 读条件
     */
    private final Condition readCondition = lock.newCondition();
    /**
     * 缓存队列
     */
    private final Object[] items = new Object[100];
    /**
     * 写索引 、 读索引、 队列中存在的数据个数
     */
    private int writeIndex, readIndex, count;

    /**
     * 写入缓存队列
     *
     * @param x 写入内容
     * @throws InterruptedException 中断异常
     */
    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            // 队列满了
            if (count == items.length) {
                // 阻塞写线程
                writeCondition.await();
            }
            items[writeIndex] = x;
            if (++writeIndex == items.length) {
                // 如果写索引写到队列的最后一个位置了，那么置为0
                writeIndex = 0;
            }
            ++count;
            // 唤醒读线程
            readCondition.signal();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 读取缓存队列
     *
     * @return 读取内容
     * @throws InterruptedException 中断异常
     */
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            if (count == 0) {
                readCondition.await();
            }
            Object x = items[readIndex];
            if (++readIndex == items.length) {
                //如果读索引读到队列的最后一个位置了，那么置为0
                readIndex = 0;
            }
            --count;
            writeCondition.signal();
            return x;
        } finally {
            lock.unlock();
        }
    }

}
